package mdb

import (
	"database/sql"
	"fmt"
	"gitee.com/dennis-mxx/mxx-core/mexception"
	"gitee.com/dennis-mxx/mxx-core/mreflect"
	"reflect"
)

var Scan = &scan{}

type scan struct {
}

func (domain *scan) ScanAll(dest interface{}, rows *sql.Rows) error {

	var value reflect.Value
	value = reflect.ValueOf(dest)
	if value.Kind() != reflect.Ptr {
		return mexception.NewException(mexception.IllegalParameterException, "must pass a pointer, not a value, to StructScan destination")
	}
	if value.IsNil() {
		return mexception.NewException(mexception.NilException, "nil pointer passed to StructScan destination")
	}
	direct := reflect.Indirect(value)
	if direct.Type().Kind() == reflect.Slice {
		slice := make([]reflect.Value, 0)
		for rows.Next() {
			val := reflect.New(direct.Type().Elem()).Interface()
			if error := domain.scanVal(val, rows); error != nil {
				return error
			}
			slice = append(slice, reflect.ValueOf(val).Elem())
		}
		elem := value.Elem()
		value.Elem().Set(reflect.Append(elem, slice...))
		return nil
	}

	return mexception.NewException(mexception.IllegalParameterException, "dest type is not Slice")
}

func (domain *scan) ScanRow(dest interface{}, rows *sql.Rows) error {
	for rows.Next() {
		return domain.scanVal(dest, rows)
	}
	return fmt.Errorf("rows is nil")
}
func (domain *scan) scanVal(dest interface{}, rows *sql.Rows) error {
	value := reflect.ValueOf(dest).Elem()
	_type := value.Type()
	if _type.Kind() != reflect.Struct {
		return mexception.NewException(mexception.IllegalParameterException, "dest type is not Struct")
	}
	mapping := mreflect.LoadTagMapping(_type, "db")
	columns, _ := rows.Columns()
	fields := make([]interface{}, len(columns))

	for i, column := range columns {
		fieldName := mapping[column]
		if fieldName == "" {
			return mexception.NewException(mexception.IllegalParameterException, fmt.Sprintf("no search Struct tag db[%s]", column))
		}

		fields[i] = value.FieldByName(fieldName).Addr().Interface()
	}
	return rows.Scan(fields...)
}
